#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include "sys_info.h"

#define DEVICE_NAME "sys_info"
#define CLASS_NAME "sys_info"


static struct class *sys_info;
static dev_t devt;
static struct cdev sys_info_cdev;

static int func_op_0(unsigned long arg){
    op0_pram_class op0_pram;
    op0_pram.pram1 = 420258212;
    op0_pram.pram2 = 3445608628;
    op0_pram.pram3 = 600;
    if (copy_to_user((void *)(uintptr_t)arg, &op0_pram, sizeof(op0_pram_class))){
        return -EFAULT;
    }
    return 0;
}

static int func_op_1(unsigned long arg){
    signed int op1_pram = 2;
    if (copy_to_user((void *)(uintptr_t)arg, &op1_pram, sizeof(signed int))){
        return -EFAULT;
    }
    return 0;
}

static int func_op_2(unsigned long arg){
    unsigned long op2_pram = jiffies;
    if (copy_to_user((void *)(uintptr_t)arg, &op2_pram, sizeof(unsigned long))){
        return -EFAULT;
    }
    return 0;
}

static int func_op_3(unsigned long arg){
    unsigned int op3_pram = 0;
    if (copy_to_user((void *)(uintptr_t)arg, &op3_pram, sizeof(int))){
        return -EFAULT;
    }
    return 0;
}

static int func_op_4(unsigned long arg){
    return 0;
}

static int func_op_5(unsigned long arg){
    op5_pram_class op5_pram;
    strncpy((char *)op5_pram.pram1, "281474957152752", strlen("281474957152752"));
    snprintf((char *)op5_pram.pram2, 64, "%s %s", BUILD_TIME, BUILD_DATE);
    if (copy_to_user((void *)(uintptr_t)arg, &op5_pram, sizeof(op5_pram_class))){
        return -EFAULT;
    }
    return 0;
}

static int func_op_6(unsigned long arg){
    unsigned int op6_pram = 15732722;
    if (copy_to_user((void *)(uintptr_t)arg, &op6_pram, sizeof(unsigned int))){
        return -EFAULT;
    }
    return 0;
}

static int func_op_7(unsigned long arg){
    return 0;
}

static int func_op_10(unsigned long arg){
    return 0;
}

static int func_op_17(unsigned long arg){
    op17_pram_class op17_pram;
    op17_pram.pram1 = 2239758336;
    op17_pram.pram2 = 8388608;
    if (copy_to_user((void *)(uintptr_t)arg, &op17_pram, sizeof(op17_pram_class))){
        return -EFAULT;
    }
    return 0;
}

static int func_op_19(unsigned long arg){
    return 0;
}

static int func_op_20(unsigned long arg){
    unsigned int op20_pram = 0;
    if (copy_to_user((void *)(uintptr_t)arg, &op20_pram, sizeof(unsigned int))){
        return -EFAULT;
    }
    return 0;
}

static int func_op_21(unsigned long arg){
    unsigned int op21_pram = 5;
    if (copy_to_user((void *)(uintptr_t)arg, &op21_pram, sizeof(unsigned int))){
        return -EFAULT;
    }
    return 0;
}

static int func_op_26(unsigned long arg){
    op17_pram_class op26_pram;
    op26_pram.pram1 = 5905;
    op26_pram.pram2 = 256;
    if (copy_to_user((void *)(uintptr_t)arg, &op26_pram, sizeof(op17_pram_class))){
        return -EFAULT;
    }
    return 0;
}

static int sys_info_open(struct inode *inode, struct file *file) {
    return 0;
}

static int sys_info_release(struct inode *inode, struct file *file) {
    return 0;
}

static long sys_info_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
    
    int ret = -ENOTTY;
    switch (cmd) {
    case SYS_INFO_ROP_0:
        ret = func_op_0(arg);
        break;
    case SYS_INFO_ROP_1:
        ret = func_op_1(arg);
        break;
    case SYS_INFO_ROP_2:
        ret = func_op_2(arg);
        break;
    case SYS_INFO_ROP_3:
        ret = func_op_3(arg);
        break;
    case SYS_INFO_ROP_4:
        ret = func_op_4(arg);
        break;
    case SYS_INFO_ROP_5:
        ret = func_op_5(arg);
        break;
    case SYS_INFO_ROP_6:
        ret = func_op_6(arg);
        break;
    case SYS_INFO_ROP_7:
        ret = func_op_7(arg);
        break;
    case SYS_INFO_ROP_10:
        ret = func_op_10(arg);
        break;
    case SYS_INFO_ROP_17:
        ret = func_op_17(arg);
        break;
    case SYS_INFO_ROP_19:
        ret = func_op_19(arg);
        break;
    case SYS_INFO_ROP_20:
        ret = func_op_20(arg);
        break;
    case SYS_INFO_ROP_21:
        ret = func_op_21(arg);
        break;
    case SYS_INFO_ROP_26:
        ret = func_op_26(arg);
        break;
    default:
        return -ENOTTY;
    }
    return ret;
}

static struct file_operations fops = {
    .open = sys_info_open,
    .release = sys_info_release,
    .unlocked_ioctl = sys_info_ioctl,
};

static int __init sys_info_init(void) {
    int ret;

    // 动态分配设备号
    ret = alloc_chrdev_region(&devt, 0, 1, DEVICE_NAME);
    if (ret < 0) {
        pr_err("Failed to allocate device number\n");
        return ret;
    }

    // 初始化并添加字符设备
    cdev_init(&sys_info_cdev, &fops);
    ret = cdev_add(&sys_info_cdev, devt, 1);
    if (ret < 0) {
        pr_err("Failed to add cdev\n");
        goto err_cdev;
    }

    // 创建设备类
    sys_info = class_create(THIS_MODULE, CLASS_NAME);
    if (IS_ERR(sys_info)) {
        ret = PTR_ERR(sys_info);
        pr_err("Failed to create class\n");
        goto err_class;
    }

    // 创建设备节点
    if (IS_ERR(device_create(sys_info, NULL, devt, NULL, DEVICE_NAME))) {
        pr_err("Failed to create device\n");
        ret = -1;
        goto err_device;
    }

    pr_info("Driver loaded\n");
    return 0;

err_device:
    class_destroy(sys_info);
err_class:
    cdev_del(&sys_info_cdev);
err_cdev:
    unregister_chrdev_region(devt, 1);
    return ret;
}

static void __exit sys_info_exit(void) {
    device_destroy(sys_info, devt);
    class_destroy(sys_info);
    cdev_del(&sys_info_cdev);
    unregister_chrdev_region(devt, 1);
    pr_info("Driver unloaded\n");
}

module_init(sys_info_init);
module_exit(sys_info_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("openUBMC-qemu");